<?php

namespace StudyBuddy\Modules\Blogger;

use \StudyBuddy\Interfaces\BloggerUser;
use \StudyBuddy\StudyBuddyObject;
use \StudyBuddy\Registry;

/**
 * Blogger API Client class
 * This class implements methods for
 * working with Blogger API
 *
 * More info here:
 * http://code.google.com/apis/blogger/docs/2.0/developers_guide_protocol.html#DeletingEntries
 *
 */
class ApiClient extends StudyBuddyObject {
    /**
     * URL template for Blogger Write API
     * The value of %s in this template will be
     * replaced with user's blog id
     *
     * @var string
     */
    const API_WRITE_URL = 'https://www.blogger.com/feeds/%s/posts/default?v=2';


    /**
     * URL for editing or deleting a post
     * %1$s = blogID, %2$s = postID
     * TO edit use PUT method, to delete use DELETE method
     * 
     * @var string
     */
    const API_ENTRY_URL = 'http://www.blogger.com/feeds/%1$s/posts/default/%2$s?v=2';


    /**
     * Url to retreive all comments for entry
     * OR to add a comment
     * TO add a comment the method should be "POST"
     * and xml of comment is:
     * <entry xmlns='http://www.w3.org/2005/Atom'>
     *   <title type="text">This is my first comment</title>
     *   <content type="html">This is my first comment</content>
     * </entry>
     *
     * %1$s should be replaced with blogID,
     * %2$s should be replaced with entryID
     *
     * @var string
     */
    const API_COMMENTS_URL = 'http://www.blogger.com/feeds/%1$s/%2$s/comments/default?v=2';


    /**
     * URL of ONE comment
     * %1$s = blogID, %2$s = postID, %3$s = commentID
     * To delete a comment use DELETE method for this url
     * Editing of comment is not supported by API and
     * not even possible via Blogger web interface! Yea, they suck
     *
     * @var string
     */
    const API_COMMENT_URL = 'http://www.blogger.com/feeds/%1$s/%2$s/comments/default/%3$s';


    /**
     *
     * OAuth
     * @var object of type OAuth
     */
    protected $oAuth;

    /**
     * BloggerUser
     *
     * @var Object of type BloggerUser
     * set ONLY if User has Blogger Oauth credentials
     */
    protected $oUser;

    /**
     * Blogger API Config array
     * This is an array of Blogger section from !config.ini
     *
     * @var array
     */
    protected $aConfig;

    /**
     * Constructor
     *
     * @param Registry $oRegistry
     * @throws \StudyBuddy\Exception if oauth extension not available in php
     * @throws \StudyBuddy\DevException if there is no BLOGGER section in !config.ini
     * @throws \StudyBuddy\Exception if something goes wrong during
     * instantiation of OAuth object (which is very unlikely)
     */
    public function __construct(Registry $oRegistry) {
        if (!extension_loaded('oauth')) {
            throw new \StudyBuddy\Exception('Cannot use this class because php extension "oauth" is not loaded');
        }

        $this->oRegistry = $oRegistry;
        $this->aConfig = $oRegistry->Ini->offsetGet('BLOGGER');
        d('$this->aConfig: ' . print_r($this->aConfig, 1));
        if (empty($this->aConfig) || empty($this->aConfig['OAUTH_KEY']) || empty($this->aConfig['OAUTH_SECRET'])) {
            throw new DevException('Missing configuration parameters for BLOGGER API');
        }

        $Viewer = $oRegistry->Viewer;
        if ($Viewer && ($Viewer instanceof BloggerUser)) {
            $this->setUser($Viewer);
        }

        try {
            d('cp');
            $this->oAuth = new \OAuth($this->aConfig['OAUTH_KEY'], $this->aConfig['OAUTH_SECRET'], OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
            $this->oAuth->enableDebug();

            d('cp');
        } catch (\OAuthException $e) {
            e('OAuthException: ' . $e->getMessage() . ' ' . print_r($e, 1));

            throw new Exception('Something went wrong during Blogger authorization. This has nothing to do with your account. Please try again later' . $e->getMessage());
        }
    }

    /**
     * Set $this->oUser but only
     * of $oUser has Blogger oauth token
     *
     * @param BloggerUser $oUser
     */
    public function setUser(BloggerUser $oUser) {

        if (null !== $oUser->getBloggerToken()) {
            $this->oUser = $oUser;
        } else {
            d('User does not have Blogger oauth token');

            return false;
        }

        return $this;
    }

    /**
     * Getter for $this->oUser
     *
     * @return object $this->oUser
     */
    public function getUser() {

        return $this->oUser;
    }

    /**
     * Add content to Blogger
     * @todo finish this to handle posting
     * of link, photo, autio, video
     *
     * @param EntryInterface $post
     *
     * @return mixed null on unexpected error | string id of new Blogger post
     */
    public function add(EntryInterface $post) {
        $xHeader = array('Content-Type' => 'application/atom+xml');
        $res = $this->apiWrite($post, $xHeader);
        $id = false;

        try {
            $o = new Entry($res);
            $id = $o->getId();
            d('id: ' . $id);
        } catch (\Exception $e) {
            e('Unable to parse result of Blogger Post: ' . $ret . ' error: ' . $e->getMessage() . ' in file: ' . $e->getFile() . ' on line: ' . $e->getLine());
        }

        return $id;
    }

    /**
     * @todo unfihished
     *
     * @param BloggerContent $post
     */
    public function edit(EntryInterface $post) {
        
    }

    /**
     * @todo unfihished
     *
     * @param BloggerContent $post
     */
    public function delete(EntryInterface $post) {
        
    }

    /**
     * Post data to Blogger API
     *
     * @param \DOMDocument $data object of type DOMDocument
     * representing the XML of $entry
     * The Entry object extends the \DOMDocument (by extending the
     * \StudyBuddy\Dom\Document so requiring this to be a DOMDocument
     * is just a way to ensure that it is a valid XML document and does
     * have the saveXML() method
     *
     * @param array $headers additional headers to send to Blogger API
     *
     * @param string $method request method. Usually POST but sometimes
     * could be "PUT" and for reading from API it is usually "GET"
     * To delete a post (or comment) the method should be "DELETE"
     *
     * @throws \LogicException if $this->oUser (BloggerUser) is not yet set
     * @throws \StudyBuddy\Exception
     *
     * @return string in case of adding blog post
     * it is id of new post (but not really a numeric id, it's
     * in raw format and actual id must still be extracted, which is
     * done in add() method
     */
    protected function apiWrite(\DOMDocument $entry, array $headers, $method = 'POST') {
        if (!isset($this->oUser)) {
            throw new \LogicException('Cannot use API because $this->oUser not set. Set BloggerUser by calling setUser(BloggerUser $o) before using this method');
        }

        $blogId = $this->oUser->getBloggerBlogId();
        $xml = $entry->saveXML();

        try {

            $this->oAuth->setAuthType(OAUTH_AUTH_TYPE_AUTHORIZATION);
            $token = $this->oUser->getBloggerToken();
            $secret = $this->oUser->getBloggerSecret();
            d('setting $token: ' . $token . ' secret: ' . $secret);

            $this->oAuth->setToken($token, $secret);
            $url = \sprintf(self::API_WRITE_URL, $this->oUser->getBloggerBlogId());
            d('fetching: ' . $url . ' entry: ' . $xml);

            $this->oAuth->fetch($url, $xml, $method, $headers);
        } catch (\OAuthException $e) {
            $aDebug = $this->oAuth->getLastResponseInfo();
            d('debug: ' . print_r($aDebug, 1));

            e('OAuthException: ' . $e->getMessage());
            /**
             * Should NOT throw Exception because
             * we are not sure it was actually due to authorization
             * or maby Blogger was bugged down or something else
             */
            throw new \StudyBuddy\Exception('Something went wrong during connection with Blogger. Please try again later. ' . $e->getMessage());
        }

        return $this->getResponse();
    }

    /**
     *
     * Extract response from Oauth, examine
     * the http response code
     * In case of 401 code - revoe user's Blogger Oauth credentials
     * @throws \StudyBuddy\DevException
     */
    protected function getResponse() {
        $ret = $this->oAuth->getLastResponse();

        $aDebug = $this->oAuth->getLastResponseInfo();
        d('debug: ' . print_r($aDebug, 1));
        if ('200' == $aDebug['http_code'] || '201' == $aDebug['http_code']) {
            d('successful post to API');

            return $ret;
        } elseif ('401' == $aDebug['http_code']) {
            d('Blogger oauth failed with 401 http code. Data: ' . print_r($aData, 1));
            /**
             * If this method was passed oUser
             * then null the tokens
             * and save the data to table
             * so that next time we will know
             * that this User does not have tokens
             */
            if (is_object($this->oUser)) {
                d('Going to revoke access tokens for user object');
                $this->oUser->revokeBloggerToken();
                /**
                 * Important to post this update
                 * so that user object will be removed from cache
                 */
                $this->oRegistry->Dispatcher->post($this->oUser, 'onBloggerTokenUpdate');
            }

            /**
             * This exception should be caught all the way in WebPage and it will
             * cause the ajax message with special key=>value which will
             * trigger the popup to be shown to user with link
             * to signing with Blogger
             */
            throw new \StudyBuddy\DevException('Blogger API OAuth credentials failed. Possibly user removed our app');
        } else {
            e('Blogger API Post failed http code was: ' . $aDebug['http_code'] . ' full debug: ' . print_r($aDebug, 1) . ' response: ' . $ret);

            throw new \StudyBuddy\DevException('Blogger OAuth post failed');
        }
    }

}
